package com.dinstar.linphone.core;

import static org.linphone.core.Call.State.OutgoingInit;

import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.TextureView;
import android.widget.Toast;

import androidx.annotation.NonNull;


import com.dinstar.linphone.R;
import com.dinstar.linphone.callback.PhoneCallback;
import com.dinstar.linphone.callback.RegistrationCallback;
import com.dinstar.linphone.utils.AudioRouteUtils;
import com.dinstar.linphone.utils.LinphoneUtils;


import org.linphone.core.AccountCreator;
import org.linphone.core.Address;
import org.linphone.core.AudioDevice;
import org.linphone.core.Call;
import org.linphone.core.CallParams;
import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.Factory;
import org.linphone.core.LogCollectionState;
import org.linphone.core.ProxyConfig;
import org.linphone.core.Reason;
import org.linphone.core.RegistrationState;
import org.linphone.core.TransportType;

import java.io.File;
import java.util.Locale;

public class LinphoneManager {
    private static LinphoneManager INSTANCE = null;
    private Call.State previousCallState = Call.State.Idle;
    private Context mContext;
    private CorePreferences corePreferences;
    private Core core;
    private RegistrationCallback registrationCallback;
    private boolean gsmCallActive = false;
//    private PhoneCallback phoneCallback;
    private boolean coreIsStart = false;
    private Call currentCall;

    public LinphoneManager() {

    }

    public static LinphoneManager getInstance() {
        if (INSTANCE == null) {
            synchronized (LinphoneUtils.class) {
                if (INSTANCE == null) {
                    INSTANCE = new LinphoneManager();
                }
            }
        }
        return INSTANCE;
    }

    public LinphoneManager init(Context context) {
        mContext = context.getApplicationContext();

        //开启日志
        Factory.instance().enableLogCollection(LogCollectionState.Enabled);
        Factory.instance().enableLogcatLogs(true);

        corePreferences = new CorePreferences(mContext);
        corePreferences.copyAssetsFromPackage();

        core = Factory.instance().createCoreWithConfig(corePreferences.getConfig(), mContext);
        return this;
    }

    /**
     * 登录到服务器
     *
     * @param username 账号名
     * @param password 密码
     * @param ip       IP地址
     * @param port     端口号
     */
    public void Login(@NonNull String username, @NonNull String password, @NonNull String ip, @NonNull String port) {
        core.clearProxyConfig();
        String domain = ip + ":" + port;
        AccountCreator accountCreator = core.createAccountCreator(corePreferences.getXmlrpcUrl());
        accountCreator.setLanguage(Locale.getDefault().getLanguage());
        accountCreator.reset();

        accountCreator.setUsername(username);//账号
        accountCreator.setPassword(password);//密码
        accountCreator.setDisplayName(username);//显示名称
        accountCreator.setDomain(domain);//域名
        accountCreator.setTransport(TransportType.Udp);//传输类型
        Log.e("========","登录信息：username "+username  +"password:"+password + "ip:"+ip + "port:"+port);
        accountCreator.createProxyConfig();
    }

    public LinphoneManager registrationCallback(RegistrationCallback registrationCallback) {
        this.registrationCallback = registrationCallback;
        return this;
    }

    public LinphoneManager registerPhoneCallback(PhoneCallback phoneCallback) {
//        this.phoneCallback = phoneCallback;
        return this;
    }

    /**
     * 启动linphone
     */
    public void start() {
        if (!coreIsStart) {
            Log.i(mContext.getString(R.string.app_name), "[LinphoneManager] starting Linphone");
            coreIsStart = true;
            initLinphone();
            core.addListener(mCoreListener);
            core.start();

            TelephonyManager telephonyManager =
                    (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
            Log.i(mContext.getString(R.string.app_name), "[LinphoneManager] 注册 phone state listener");
            telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
        }
    }

    /***
     * 初始化设置
     */
    private void initLinphone() {
        configureCore();
//        initUserCertificates();
    }

    private void configureCore() {
        core.setNativeRingingEnabled(true);// 来电铃声
        core.setVibrationOnIncomingCallEnabled(true);// 来电振动
        core.enableEchoCancellation(true); //回声消除
        core.enableAdaptiveRateControl(true); //自适应码率控制
    }

    /***
     * 设置存放用户x509证书的目录路径
     */
    private void initUserCertificates() {
        String userCertsPath = corePreferences.userCertificatesPath;
        File f = new File(userCertsPath);
        if (!f.exists()) {
            if (!f.mkdir()) {
                Log.e(mContext.getString(R.string.app_name), "[LinphoneManager] " + userCertsPath + "不能创建");
            }
        }
        core.setUserCertificatesPath(userCertsPath);
    }

    /**
     * 拨打电话
     *
     * @param toPhoneNum  String
     * @param isVideoCall Boolean
     */
    public void startCall(@NonNull String toPhoneNum, @NonNull boolean isVideoCall) {
        if (toPhoneNum.trim().isEmpty()) {
            Toast.makeText(mContext, "请输入正确的呼叫号码", Toast.LENGTH_LONG).show();
            return;
        }
        try {
            Address addressToCall = core.interpretUrl(toPhoneNum);
            if (addressToCall != null) {
                addressToCall.setDisplayName(toPhoneNum);
            }

            CallParams params = core.createCallParams(null);
            if (params != null) {
                //启用通话录音
//            params.setRecordFile(LinphoneUtils.getRecordingFilePathForAddress(mContext, addressToCall));
                //启动低宽带模式
                if (LinphoneUtils.checkIfNetworkHasLowBandwidth(mContext)) {
                    Log.w(mContext.getString(R.string.app_name), "[LinphoneManager] Enabling low bandwidth mode!");
                    params.enableLowBandwidth(true);
                }
                if (isVideoCall) {
                    params.enableVideo(true);
                    core.enableVideoCapture(true);
                    core.enableVideoDisplay(true);
                } else {
                    params.enableVideo(false);
                }
                core.inviteAddressWithParams(addressToCall, params);
            } else {
                core.inviteAddress(addressToCall);
            }


        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 接听来电
     */
    public int answerCall(Call call) {
        Log.i(mContext.getString(R.string.app_name), "[LinphoneManager] 接听来电 " + call);
        CallParams params = core.createCallParams(call);
        if (params != null) {
            //启用通话录音
//        params.setRecordFile(LinphoneUtils.getRecordingFilePathForAddress(mContext, call.getRemoteAddress()));
            if (LinphoneUtils.checkIfNetworkHasLowBandwidth(mContext)) {
                Log.w(mContext.getString(R.string.app_name), "[LinphoneManager] 启用低带宽模式!");
                params.enableLowBandwidth(true);
            }
            params.enableVideo(isVideoCall(call));
            return call.acceptWithParams(params);
        } else {
            return -1;
        }
    }

    /**
     * 拒接电话
     *
     * @param call Call
     */
    public int declineCall(Call call) {
        String voiceMailUri = corePreferences.getVoiceMailUri();
        if (voiceMailUri != null && corePreferences.isRedirectDeclinedCallToVoiceMail()) {
            Address voiceMailAddress = core.interpretUrl(voiceMailUri);
            if (voiceMailAddress != null) {
                Log.i(mContext.getString(R.string.app_name), "[LinphoneManager] 重定向 call " + call +" 语音信箱URI: " + voiceMailUri);
                return call.redirectTo(voiceMailAddress);
            }
        } else {
            Log.i(mContext.getString(R.string.app_name), "[LinphoneManager] 拒接电话"+call);
            return call.decline(Reason.Declined);
        }
        return -1;
    }

    /**
     * 挂断电话
     */
    public int terminateCall(Call call) {
        Log.i(mContext.getString(R.string.app_name), "[LinphoneManager] 挂断电话 " + call);
        return call.terminate();
    }

    /***
     * 麦克风是否开启
     * @return
     */
    public boolean micEnabled() {
        return core.micEnabled();
    }

    /***
     * 扬声器是否开启
     * @return
     */
    public boolean speakerEnabled() {
        return core.getOutputAudioDevice().getType() == AudioDevice.Type.Speaker;
    }

    public boolean isBluetoothAudioRouteAvailable(){
        return AudioRouteUtils.isBluetoothAudioRouteAvailable(core);
    }

    public boolean isBluetoothAudioRouteCurrentlyUsed(){
        return AudioRouteUtils.isBluetoothAudioRouteCurrentlyUsed(core);
    }

    /**
     * 启动麦克风
     *
     * @param micEnabled Boolean
     */
    public void enableMic(Boolean micEnabled) {
        if (core != null) {
            core.enableMic(micEnabled);
        }
    }

    /**
     * 扬声器或听筒/蓝牙
     *
     * @param SpeakerEnabled Boolean
     */
    public void enableSpeaker(boolean SpeakerEnabled) {
        if (core != null) {
            if (SpeakerEnabled) {
                AudioRouteUtils.routeAudioToSpeaker(core);
            } else {
                if (AudioRouteUtils.isBluetoothAudioRouteAvailable(core)){
                    AudioRouteUtils.routeAudioToBluetooth(core);
                } else {
                    AudioRouteUtils.routeAudioToEarpiece(core);
                }
            }
        }
    }

    /**
     * 是否是视频电话
     *
     * @return Boolean
     */
    public boolean isVideoCall(Call call) {
        CallParams remoteParams = call.getRemoteParams();
        return remoteParams != null && remoteParams.videoEnabled();
    }

    /**
     * 设置视频界面
     *
     * @param videoRendering TextureView 对方界面
     * @param videoPreview   CaptureTextureView 自己界面
     */
    public void setVideoWindowId(TextureView videoRendering, TextureView videoPreview) {
        if (core != null) {
            core.setNativeVideoWindowId(videoRendering);
            core.setNativePreviewWindowId(videoPreview);
        }
    }

    /**
     * 停止linphone
     */
    private void stop() {
        coreIsStart = false;
        TelephonyManager telephonyManager =
                (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);

        Log.i(mContext.getString(R.string.app_name), "[LinphoneManager] 注销 phone state listener");
        telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);

        core.removeListener(mCoreListener);
        core.stop();
    }

    /**
     * 注销登录
     */
    public void loginOut() {
        if (core != null) {
            core.clearProxyConfig();
        }
    }

    public Call getCurrentCall() {
        return (currentCall != null) ? currentCall : null;
    }

    public void getLoginState() {
        if (core != null && core.getDefaultAccount() != null){
            Intent intent = new Intent("com.okcis.siplibrary.LoginState");
            intent.putExtra("LoginState", core.getDefaultAccount().getState().toInt());
            mContext.sendBroadcast(intent);
        }
    }

    CoreListenerStub mCoreListener = new CoreListenerStub() {
        @Override
        public void onRegistrationStateChanged(@NonNull Core core, @NonNull ProxyConfig proxyConfig, RegistrationState state, @NonNull String message) {
            super.onRegistrationStateChanged(core, proxyConfig, state, message);
            Intent intent = new Intent("com.okcis.siplibrary.LoginState");
            switch (state) {
                case None:
                    intent.putExtra("LoginState", RegistrationState.None.toInt());
                    mContext.sendBroadcast(intent);
//                    registrationCallback.registrationNone();
                    break;
                case Progress:
                    intent.putExtra("LoginState", RegistrationState.Progress.toInt());
                    mContext.sendBroadcast(intent);
//                    registrationCallback.registrationProgress();
                    break;
                case Ok:
                    intent.putExtra("LoginState", RegistrationState.Ok.toInt());
                    mContext.sendBroadcast(intent);
//                    registrationCallback.registrationOk();
                    break;
                case Cleared:
                    intent.putExtra("LoginState", RegistrationState.Cleared.toInt());
                    mContext.sendBroadcast(intent);
//                    registrationCallback.registrationClear();
                    break;
                case Failed:
                    intent.putExtra("LoginState", RegistrationState.Failed.toInt());
                    mContext.sendBroadcast(intent);
//                    registrationCallback.registrationFailed();
                    break;
            }
        }


        @Override
        public void onCallStateChanged(@NonNull Core core, @NonNull Call call, Call.State state, @NonNull String message) {
            super.onCallStateChanged(core, call, state, message);
            Log.e(mContext.getString(R.string.app_name), "[LinphoneManager] Call state changed " + state + "     " + core.getCallsNb());
            if (!gsmCallActive) {
                currentCall = call;
            }
            Intent intent = new Intent("com.okcis.siplibrary.CallState");
            intent.setPackage(mContext.getPackageName());
            switch (state) {
                case IncomingReceived:
                case IncomingEarlyMedia:
                    if (gsmCallActive) {
                        Log.w(mContext.getString(R.string.app_name), "[LinphoneManager] Refusing the call with reason busy because a GSM call is active");
                        call.decline(Reason.Busy);
                        return;
                    }
                    intent.putExtra("CallState", Call.State.IncomingReceived.toInt());
                    mContext.sendBroadcast(intent);
//                    phoneCallback.incomingCall(currentCall);
                    gsmCallActive = true;
                    //自动接听
                    if (corePreferences.isAutoAnswerEnabled()) {
                        int autoAnswerDelay = corePreferences.getAutoAnswerTime();
                        if (autoAnswerDelay == 0) {
                            Log.w(mContext.getString(R.string.app_name), "[LinphoneManager] 立即自动接听来电");
                            answerCall(currentCall);
                        } else {
                            Log.i(mContext.getString(R.string.app_name), "[LinphoneManager] " + autoAnswerDelay + "ms后自动接听来电");
                            Handler mainThreadHandler = new Handler(Looper.getMainLooper());
                            mainThreadHandler.postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    Log.w(mContext.getString(R.string.app_name), "[LinphoneManager] Auto answering call");
                                    answerCall(currentCall);
                                }
                            }, autoAnswerDelay);
                        }
                    }
                    break;
                case OutgoingInit:
                    if (core.getCallsNb() == 1 && !gsmCallActive) {
                        intent.putExtra("CallState", Call.State.OutgoingInit.toInt());
                        mContext.sendBroadcast(intent);
//                        phoneCallback.outgoingInit(currentCall);
                        gsmCallActive = true;
                    }
                    break;
                case OutgoingProgress:
                    if (core.getCallsNb() == 1 && corePreferences.isRouteAudioToBluetoothIfAvailable()) {
                        AudioRouteUtils.routeAudioToBluetooth(core);
                    }
                    break;
                case Connected:
                    intent.putExtra("CallState", Call.State.Connected.toInt());
                    mContext.sendBroadcast(intent);
//                    phoneCallback.callConnected(currentCall);
                    break;
                case StreamsRunning:
                    if (core.getCallsNb() == 1) {
                        // 只有第一次呼叫在StreamsRunning时才尝试连接蓝牙或耳机
                        if (previousCallState == Call.State.Connected) {
                            Log.i(mContext.getString(R.string.app_name), "[LinphoneManager] 第一次进入StreamsRunning状态，尝试将音频路由到可用的耳机或蓝牙设备");
                            if (!getInstance().speakerEnabled()){//手动开启扬声器时，接通后不切换耳机或者蓝牙
                                if (AudioRouteUtils.isHeadsetAudioRouteAvailable(core)) {
                                    AudioRouteUtils.routeAudioToHeadset(core);
                                } else if (corePreferences.isRouteAudioToBluetoothIfAvailable() && AudioRouteUtils.isBluetoothAudioRouteAvailable(core)) {
                                    AudioRouteUtils.routeAudioToBluetooth(core);
                                }
                            }
                        }
                    }

                    if (corePreferences.isRouteAudioToSpeakerWhenVideoIsEnabled() && currentCall.getCurrentParams().videoEnabled()) {
                        // 如果使用耳机或蓝牙，在启用视频时不打开扬声器
                        if (!AudioRouteUtils.isHeadsetAudioRouteAvailable(core) &&
                                !AudioRouteUtils.isBluetoothAudioRouteCurrentlyUsed(core)
                        ) {
                            Log.i(mContext.getString(R.string.app_name), "[LinphoneManager] 视频通话，没有有线耳机，没有使用蓝牙，音频路由到扬声器");
                            AudioRouteUtils.routeAudioToSpeaker(core);
                        }
                    }
                    break;
                case End:
                    if (core.getCallsNb() == 0) {
                        intent.putExtra("CallState", Call.State.End.toInt());
                        mContext.sendBroadcast(intent);
//                        phoneCallback.callEnd(currentCall);
                        gsmCallActive = false;
                    }
                    break;
                case Released:
                    if (core.getCallsNb() == 0) {
                        intent.putExtra("CallState", Call.State.Released.toInt());
                        mContext.sendBroadcast(intent);
//                        phoneCallback.callReleased(currentCall);
                        gsmCallActive = false;
                    }
                    break;
                case Error:
                    if (core.getCallsNb() == 0) {
                        int msgId;
                        switch (currentCall.getErrorInfo().getReason()) {
                            case Busy:
                                msgId = R.string.call_error_user_busy;
                                break;
                            case IOError:
                                msgId = R.string.call_error_io_error;
                                break;
                            case NotAcceptable:
                                msgId = R.string.call_error_incompatible_media_params;
                                break;
                            case NotFound:
                                msgId = R.string.call_error_user_not_found;
                                break;
                            case Forbidden:
                                msgId = R.string.call_error_forbidden;
                                break;
                            default:
                                msgId = R.string.call_error_unknown;
                                break;
                        }
                        intent.putExtra("CallState", Call.State.Error.toInt());
                        mContext.sendBroadcast(intent);
//                        phoneCallback.error(mContext.getString(msgId));
                        gsmCallActive = false;
                    }
                    break;

            }
            previousCallState = state;
        }
    };

    PhoneStateListener phoneStateListener = new PhoneStateListener() {
        @Override
        public void onCallStateChanged(int state, String phoneNumber) {
            super.onCallStateChanged(state, phoneNumber);
            Log.i(mContext.getString(R.string.app_name), "[LinphoneManager] 手机状态 " + state);
            switch (state) {
                case TelephonyManager.CALL_STATE_OFFHOOK:
                case TelephonyManager.CALL_STATE_RINGING:
                    gsmCallActive = true;
                    break;
                default:
                    gsmCallActive = false;
                    break;
            }
        }
    };


}
